Home:ALL Converter>Possible to do an `in` `lookup_type` through the django-filter URL parser?

Possible to do an `in` `lookup_type` through the django-filter URL parser?

Ask Time:2014-06-04T23:31:57         Author:Ross Rogers

Json Formatter

I'm using django-filter with django-rest-framework and I'm trying to instantiate a filter that accepts lists of numbers for filtering the query set down

class MyFilter(django_filters.FilterSet):   
    ids = django_filters.NumberFilter(name='id',lookup_type='in')
    class Meta:
        model = MyModel
        fields = ('ids',)

class MyModelViewSet(viewsets.ModelViewSet):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer
    filter_class = MyFilter

If I pass in a comma separated list of integers, the filter is ignored altogether.

If I pass in a single integer, it gets through django-filter into django's form validator and complains:

'Decimal' object is not iterable

Is there a way to create a django-filter object which can handle a list of integers and properly filter down the queryset?

Author:Ross Rogers,eproduced under the CC 4.0 BY-SA copyright license with a link to the original source and this disclaimer.
Link to original article:https://stackoverflow.com/questions/24041639/possible-to-do-an-in-lookup-type-through-the-django-filter-url-parser
Ross Rogers :

For better or worse, I created a custom filter for this:\n\nclass IntegerListFilter(django_filters.Filter):\n def filter(self,qs,value):\n if value not in (None,''):\n integers = [int(v) for v in value.split(',')]\n return qs.filter(**{'%s__%s'%(self.name, self.lookup_type):integers})\n return qs\n\n\nWhich is used like:\n\nclass MyFilter(django_filters.FilterSet): \n ids = IntegerListFilter(name='id',lookup_type='in')\n class Meta:\n model = MyModel\n fields = ('ids',)\n\nclass MyModelViewSet(viewsets.ModelViewSet):\n queryset = MyModel.objects.all()\n serializer_class = MyModelSerializer\n filter_class = MyFilter\n\n\nNow my interface accepts comma-delimited lists of integers.",
2014-06-04T15:58:16
Diesel :

I know this is an old post, but there is now a better solution. The change that makes it correct is posted here. \n\nThey added a BaseInFilter and a BaseRangeFilter. The documentation is here.\n\nBig picture, BaseFilter checks for CSV, and then when mixed with another filter it does what you are asking. Your code can now be written like:\n\nclass NumberInFilter(filters.BaseInFilter, filters.NumberFilter):\n pass\n\nclass MyModelViewSet(viewsets.ModelViewSet):\n ids = NumberInFilter(name='id', lookup_expr='in')\n\n class Meta:\n model = MyModel\n fields = ['ids']\n",
2017-11-18T21:33:13
yndolok :

Here's a complete solution:\n\nfrom django_filters import Filter, FilterSet\nfrom rest_framework.filters import DjangoFilterBackend\nfrom rest_framework.viewsets import ModelViewSet\nfrom .models import User\nfrom .serializers import UserSerializer\n\n\nclass ListFilter(Filter):\n\n def filter(self, qs, value):\n if not value:\n return qs\n\n self.lookup_type = 'in'\n values = value.split(',')\n return super(ListFilter, self).filter(qs, values)\n\n\nclass UserFilter(FilterSet):\n ids = ListFilter(name='id')\n\n class Meta:\n model = User\n fields = ['ids']\n\n\nclass UserViewSet(viewsets.ModelViewSet):\n serializer_class = UserSerializer\n queryset = User.objects.all()\n filter_backends = (DjangoFilterBackend,)\n filter_class = UserFilter\n",
2015-03-03T22:02:16
Michael B :

According to a post in the django-filter issues:\n\nfrom django_filters import Filter\nfrom django_filters.fields import Lookup\n\nclass ListFilter(Filter):\n def filter(self, qs, value):\n return super(ListFilter, self).filter(qs, Lookup(value.split(u\",\"), \"in\"))\n\n\nI have personally used this without any issue in my projects, and it works without having to create a per-type filter.",
2015-02-20T23:23:33
jsmedmar :

Based on @yndolok answer I have come to a general solution. I think filtering by a list of ids is a very common task and therefore should be included in the FilterBackend:\n\nclass ListFilter(django_filters.Filter):\n\n \"\"\"Class to filter from list of integers.\"\"\"\n\n def filter(self, qs, value):\n \"\"\"Filter function.\"\"\"\n if not value:\n return qs\n self.lookup_type = 'in'\n try:\n map(int, value.split(','))\n return super(ListFilter, self).filter(qs, value.split(','))\n except ValueError:\n return super(ListFilter, self).filter(qs, [None])\n\n\nclass FilterBackend(filters.DjangoFilterBackend):\n\n \"\"\"A filter backend that includes ListFilter.\"\"\"\n\n def get_filter_class(self, view, queryset=None):\n \"\"\"Append ListFilter to AutoFilterSet.\"\"\"\n filter_fields = getattr(view, 'filter_fields', None)\n\n if filter_fields:\n class AutoFilterSet(self.default_filter_set):\n ids = ListFilter(name='id')\n\n class Meta:\n model = queryset.model\n fields = list(filter_fields) + [\"ids\"]\n\n return AutoFilterSet\n\n else:\n return super(FilterBackend, self).get_filter_class(view, queryset)\n",
2016-03-04T21:29:31
Nabat Farsi :

Uptodate solution:\n\nfrom django_filters import rest_framework as filters\n\nname-->field_name\n\nlookup_type-->lookup_expr\n\nclass IntegerListFilter(filters.Filter):\n def filter(self,qs,value):\n if value not in (None,''):\n integers = [int(v) for v in value.split(',')]\n return qs.filter(**{'%s__%s'%(self.field_name, self.lookup_expr):integers})\n return qs\n\nclass MyFilter(filters.FilterSet): \n ids = IntegerListFilter(field_name='id',lookup_expr='in')\n class Meta:\n model = MyModel\n fields = ('ids',)\n\nclass MyModelViewSet(viewsets.ModelViewSet):\n queryset = MyModel.objects.all()\n serializer_class = MyModelSerializer\n filter_class = MyFilter\n",
2019-04-08T02:58:21
yy